package com.mrx.xfriendship.telnet.server;


import com.mrx.xfriendship.telnet.command.AbstractCommand;
import com.mrx.xfriendship.telnet.command.Command;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.Banner;
import org.springframework.util.ReflectionUtils;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;

import static com.mrx.xfriendship.telnet.Constants.DELIMITER;
import static com.mrx.xfriendship.telnet.Constants.HINT;
import static com.mrx.xfriendship.telnet.TelnetUtils.getStringWithDelimiter;

/**
 * @author Mr.X
 * @since 2023-01-23 11:46
 */
@Sharable
public class TelnetServerHandler extends SimpleChannelInboundHandler<String> {

    private static final Logger logger = LoggerFactory.getLogger(TelnetServerHandler.class);

    private static final ThreadLocal<Boolean> logged = ThreadLocal.withInitial(() -> false);

    private static final ThreadLocal<String> loggedUser = ThreadLocal.withInitial(() -> "UNKNOWN");

    private final BiFunction<String, String, Boolean> loginHandler;

    private final List<AbstractCommand> commandList;

    private final String banner;

    @SneakyThrows
    public TelnetServerHandler(BiFunction<String, String, Boolean> loginHandler, List<AbstractCommand> commandList) {
        this.loginHandler = loginHandler;
        this.commandList = commandList;
        Banner instance = (Banner) ReflectionUtils.accessibleConstructor(Class.forName("org.springframework.boot.SpringBootBanner")).newInstance();
        ByteArrayOutputStream ous = new ByteArrayOutputStream();
        instance.printBanner(null, null, new PrintStream(ous));
        banner = ous.toString().replace("\n", "\r\n");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        logger.debug("连接到 Telnet 服务器: {}", ctx.name());
        ctx.write(banner);
        ctx.writeAndFlush(getStringWithDelimiter("请输入 用户名@密码 来登录控制台"));
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.warn("Telnet socket 异常关闭 ", cause);
        ctx.close();
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, String request) {
        try {
            handleResponse(request, (response, close) -> {
                ChannelFuture future = ctx.write(response);
                if (close) future.addListener(ChannelFutureListener.CLOSE);
            });
        } catch (Exception e) {
            logger.warn("Telnet 服务器出现异常: ", e);
            ctx.write(getStringWithDelimiter("输入有误!")).addListener(ChannelFutureListener.CLOSE);
        }
    }

    private void handleResponse(String request, BiConsumer<String, Boolean> whetherClose) {
        logger.debug("收到消息: {}", request);
        String response;
        boolean close = false;
        if (request.isEmpty() && !logged.get()) {
            // 如果没有登录, 且输入的内容为 空
            response = getStringWithDelimiter("请输入 用户名@密码 来登录控制台");
        } else if ("bye".equalsIgnoreCase(request)) {
            // 处理 退出 指令
            response = "再见!";
            close = true;
        } else if (logged.get()) {
            response = getStringWithDelimiter("指令错误") + DELIMITER + loggedUser.get() + HINT;
            // 处理登录成功后的指令
            for (AbstractCommand command : commandList) {
                Class<?> clazz = command.getClass();
                if (!clazz.isAnnotationPresent(Command.class)) continue;
                String[] support = clazz.getAnnotation(Command.class).value();
                if (support.length == 0) continue;
                if (Arrays.asList(support).contains(request)) {
                    response = command.process(request) + DELIMITER + loggedUser.get() + HINT;
                    break;
                }
            }
        } else {
            // 处理 登录 操作
            String[] split = request.split("@");
            if (loginHandler.apply(split[0], split[1])) {
                logged.set(true);
                loggedUser.set(split[0]);
                response = getStringWithDelimiter("登录成功") + DELIMITER + loggedUser.get() + HINT;
            } else {
                response = getStringWithDelimiter("登录失败");
            }
        }
        whetherClose.accept(response, close);
    }

}